'use strict';

var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');

/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
  // 默认配置
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

/**
 * Dispatch a request
 *
 * @param {Object} config The config specific for this request (merged with this.defaults)

    request 方法为核心，其他的别名方法都是调用的 request
    支持 request(url, config) 和 request(config) 调用

 */
Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  // 合并配置，合并时，传入配置优先级高于默认配置
  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  // 调用栈： chain 数组，dispatchRequest 为发起请求的任务，undefined 为占位用途
  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  // 通过 Primise.resolve 方法创建一个 resolved 状态的 promise，传入 config
  var promise = Promise.resolve(config);

  // 处理请求 和 响应拦截器，将请求拦截器 存入 chain 的左端，响应拦截器存入 chain 右端
  // 多个请求拦截时，先压入 chain 的请求拦截会后执行，chain 为
  // [
  //   requestFulfilledFn, requestRejectedFn,
  //   ...
  //   dispatchRequest, undefined,
  //   ...
  //   responseFulfilledFn, responseRejectedFn,
  // ]
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  // 从 chain 中两两 shift() 出来分别做 then 的成功和失败回调，通过 promise 实现链式调用
  // 请求拦截器中必须 return config 给下一个 promise.then
  // 响应拦截器中必须 return response 给下一个 promise.then
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

Axios.prototype.getUri = function getUri(config) {
  config = mergeConfig(this.defaults, config);
  return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};

// 给 Axios 原型的 request 添加别名方法，使其实例能够使用 axios.delete, axios.get ...
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

module.exports = Axios;
